So sánh với phép xử trí lỗi Assertion (phát triển phần mềm)

Asssertion khác biệt với phép xử trí lỗi thường dùng. Assertion có tác dụng ghi chép lại các tình huống không thể nào về mặt logic và phát hiện ra lỗi lập trình: nếu điều không thể mà lại xảy ra, thì rõ ràng chương trình có vấn đề cơ bản gì đó. Điều này khác biệt hẳn với phép xử trí lỗi: hầu hết tình huống lỗi đều có thể xảy ra, mặc dù một số tình huống có thể cực kỳ khó mà xảy ra trong thực tế. Sử dụng assertion làm cơ chế xử trí lỗi đa dụng là điều không sáng suốt: assertion không hề cho phép hồi phục từ trạng thái lỗi; sự bất thành của assertion thường sẽ ngưng sự thực thi chương trình lại một cách đột ngột; và assertion còn thường hay bị vô hiệu hóa trong code sản xuất. Assertion cũng không hiển thị thông báo lỗi thân thiện với người dùng.

Xem xét ví dụ sau về việc sử dụng assertion để xử trí lỗi:

  int *ptr = malloc(sizeof(int) * 10);  assert(ptr);  // rồi dùng ptr thế nào đó  ...

Ở đây, lập trình viên nhận thức được rằng malloc sẽ trả về một con trỏ NULL nếu bộ nhớ không được cấp phát. Điều này có thể xảy ra: hệ điều hành không hề đảm bảo rằng mọi lời gọi tới malloc đều sẽ thành công. Nếu xảy ra lỗi hết bộ nhớ, chương trình sẽ ngay lập tức dừng ngang. Nếu không có assertion, chương trình sẽ tiếp tục chạy cho đến khi ptr được khử tham chiếu, và có thể còn lâu hơn, tùy thuộc vào phần cứng cụ thể đang được sử dụng. Miễn là assertion không bị vô hiệu hóa thì đảm bảo chương trình sẽ thoát ra ngay. Nhưng nếu mong muốn tác vụ được bất thành một cách yên ổn thì chương trình phải xử trí sự bất thành đó. Ví dụ: server nào đó có thể có nhiều máy khách, hoặc có thể giữ tài nguyên mà sẽ không được giải phóng sạch sẽ, hoặc nó có thể có những thay đổi chưa được commit để ghi vào kho dữ liệu. Trong những trường hợp như vậy, làm cho giao dịch bị bất thành (cùng với hồi phục về trạng thái trước lỗi) thì tốt hơn là đột ngột dừng ngang.

Một sai lầm khác là dựa dẫm vào tác dụng phụ của biểu thức được sử dụng làm đối số cho assertion. Ta nên luôn ghi nhớ rằng assertion có thể có lúc không hề được thực thi tí nào, vì nó chỉ có mỗi mục đích là xác minh rằng điều kiện nào đó nên luôn là true sẽ luôn luôn là true mà thôi. Bởi vậy, nếu chương trình được coi là không có lỗi và được phát hành thì assertion có thể được tắt đi và sẽ không còn được tính giá trị nữa.

Xem xét một phiên bản khác của ví dụ trước:

 int *ptr; // Câu lệnh dưới đây chạy không được nếu malloc() trả về NULL, // nhưng khi biên dịch bằng -NDEBUG thì sẽ không được thực thi! assert(ptr = malloc(sizeof(int) * 10)); // nếu biên dịch bằng -NDEBUG thì ptr sẽ không được khởi tạo ở đây! ...

Đây có vẻ là một cách trông rất thông minh để gán giá trị trả về của malloc vào ptr và kiểm tra xem nó có NULL hay không bằng đúng một bước, nhưng lời gọi malloc và việc gán vàoptr đều là tác dụng phụ của việc tính giá trị biểu thức tạo nên điều kiện assert. Khi tham số NDEBUG được truyền cho trình biên dịch, khi chương trình được coi là không có lỗi và được phát hành, câu lệnh assert() sẽ bị xóa, nên malloc() sẽ không được gọi, khiến cho ptr không được khởi tạo. Điều này có thể dẫn đến segmentation fault hoặc lỗi tương tự là con trỏ null, những lỗi này mãi về sau trong dòng thực thi chương trình mới xuất hiện, gây nên những bug có thể lác đác và rất khó lần theo. Lập trình viên đôi khi sử dụng phương cách tương tự là define VERIFY(X) để giảm thiểu vấn đề này.

Các trình biên dịch hiện đại có thể sẽ đưa ra cảnh báo khi bắt gặp đoạn mã như trên.[6]

Tài liệu tham khảo

WikiPedia: Assertion (phát triển phần mềm) http://www.jaggersoft.com/pubs/CVu11_3.html http://docs.oracle.com/javase/8/docs/technotes/gui... http://docs.oracle.com/javase/8/docs/technotes/gui... http://sunnyday.mit.edu/16.355/Hoare-CACM-69.pdf http://queue.acm.org/detail.cfm?id=2220317 http://dlang.org/version.html#StaticAssert http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arn... http://discovery.ucl.ac.uk/4991/1/4991.pdf https://library.ias.edu/files/pdfs/ecp/planningcod... https://web.archive.org/web/2021*/http://lambda-th...